package com.hyt.it.ogt.kq.service.gov.service.impl;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import javax.annotation.Resource;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.kq.common.config.ConfigManager;
import com.hyt.it.ogt.kq.common.config.exception.KqException;
import com.hyt.it.ogt.kq.common.exception.GovInvalidRequestException;
import com.hyt.it.ogt.kq.common.gov.constant.CodeEnum;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.common.gov.enums.PaperTypeEnum;
import com.hyt.it.ogt.kq.common.gov.utils.FileUtil;
import com.hyt.it.ogt.kq.common.gov.utils.TextUtil;
import com.hyt.it.ogt.kq.common.gov.utils.XmlUtil;
import com.hyt.it.ogt.kq.service.gov.mapper.PaperMapper;
import com.hyt.it.ogt.kq.service.gov.model.dto.PaperFileDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.TimeSubjectPaperDTO;
import com.hyt.it.ogt.kq.service.gov.model.entity.Paper;
import com.hyt.it.ogt.kq.service.gov.model.entity.Task;
import com.hyt.it.ogt.kq.service.gov.model.entity.Time;
import com.hyt.it.ogt.kq.service.gov.model.entity.TimeSubject;
import com.hyt.it.ogt.kq.service.gov.model.entity.TimeSubjectPaper;
import com.hyt.it.ogt.kq.service.gov.model.param.PaperDeleteParam;
import com.hyt.it.ogt.kq.service.gov.model.param.PaperParam;
import com.hyt.it.ogt.kq.service.gov.model.vo.PaperVO;
import com.hyt.it.ogt.kq.service.gov.service.IPaperService;
import com.hyt.it.ogt.kq.service.gov.service.ISubjectService;
import com.hyt.it.ogt.kq.service.gov.service.ISysFileService;
import com.hyt.it.ogt.kq.service.gov.service.ITaskService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeSubjectPaperService;
import com.hyt.it.ogt.kq.service.gov.service.ITimeSubjectService;
import com.hyt.it.ogt.kq.service.model.entity.Subject;
import com.hyt.model.PageParam;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.io.inputstream.ZipInputStream;
import net.lingala.zip4j.model.FileHeader;

/**
 * <p>
 * 试卷信息表 服务实现类
 * </p>
 *
 * @author liuq
 * @since 2021-04-27
 */
@Service
@Slf4j
public class PaperServiceImpl extends BaseServiceImpl<PaperMapper, Paper> implements IPaperService {

	@Resource
	private ISubjectService iSubjectService;

	@Resource
	private ITimeSubjectService iTimeSubjectService;

	@Resource
	private ConfigManager configManager;

	@Resource
	private ISysFileService iSysFileService;

	@Resource
	private ITaskService iTaskService;

	@Resource
	private ITimeSubjectPaperService iTimeSubjectPaperService;

	@Resource
	private ITimeService iTimeService;

	@Override
	public Page<PaperVO> paperPage(PageParam<PaperVO> pageParam, String taskId) {
		return baseMapper.page(pageParam, taskId, CodeEnum.FORMAL_PAPER.getCode());
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void importPaper(MultipartFile file, String taskId, String timeSubjectId) throws Exception {
		//获取批次科目试卷DTO
		TimeSubjectPaperDTO timeSubjectPaperDTO = baseMapper
				.getTimeSubjectPaperDTO(taskId, timeSubjectId, CodeEnum.FORMAL_PAPER.getCode());
		//校验是否已经导入试卷
		boolean hasPaper = Optional.ofNullable(timeSubjectPaperDTO)
				.map(dto -> StringUtils.isNotBlank(dto.getPaperId()))
				.orElseThrow(() -> new KqException(ResponseCode.ERROR_PAPER_IMPORT.getCode(),
						"批次科目试卷DTO数据不能为空"));
		if (hasPaper) {
			//批次科目下已有试卷
			throw new KqException(ResponseCode.ERROR_PAPER_EXISTS.getCode(),
					ResponseCode.ERROR_PAPER_EXISTS.getMsg());
		}
		//导入不同类型的试卷
		File tempFile = null;
		try {
			//1.创建临时文件
			tempFile = File.createTempFile("sj_file_" + UUIDUtils.newSortUUID(), ".zip");
			log.info("#试卷临时文件所在本地路径： {}", tempFile.getCanonicalPath());
			file.transferTo(tempFile);
			//2.解密试卷，修改文件，加密试卷
 			StringBuffer password = new StringBuffer();
			ZipFile zipFile = getZipFile(tempFile, password);
			//解析试卷类型（海云天试卷、山大欧玛试卷...）
			switch (this.analysisPaperType(zipFile)) {
				//保存海云天财政部试卷
				case SEASKYLAND_NETS:
					this.saveSeaskylightNetsPaper(zipFile, timeSubjectPaperDTO, password, file.getOriginalFilename());
					break;
				//保存山大欧玛试卷
				case OUMA_SOFT:
					this.saveOumaPaper(zipFile, timeSubjectPaperDTO, password, file);
					break;
				default:
					throw new KqException(ResponseCode.PAPER_TYPE_DOES_NOT_SUPPORT.getCode(),
							ResponseCode.PAPER_TYPE_DOES_NOT_SUPPORT.getMsg());
			}
		} finally {
			//删除临时文件
			if (null != tempFile) {
				FileUtil.deleteFileOrDir(tempFile);
			}
		}
	}

	/**
	 * 解析试卷类型（海云天试卷、山大欧玛试卷...）
	 * @param zipFile 试卷包
	 * @return 试卷类型标识
	 */
	private PaperTypeEnum analysisPaperType(ZipFile zipFile) throws ZipException {
		List<FileHeader> fileHeaders = zipFile.getFileHeaders();
		boolean hasPaperInfo = fileHeaders.stream()
				.anyMatch(fileHeader -> "paperinfo.xml".equals(fileHeader.getFileName()));
		return hasPaperInfo ? PaperTypeEnum.SEASKYLAND_NETS : PaperTypeEnum.OUMA_SOFT;
	}

	/**
	 * 保存海云天财政部试卷
	 * @param zipFile 试卷包
	 * @param timeSubjectPaperDTO 批次科目试卷DTO
	 * @param password 密码
	 * @throws Exception 异常
	 */
	private void saveSeaskylightNetsPaper(ZipFile zipFile, TimeSubjectPaperDTO timeSubjectPaperDTO,
										  StringBuffer password, String originalFilename) throws Exception {
		//更新海云天财政部试卷批次编号
		Paper paper = this.updateSeaskylightNetsPaperTimeCode(zipFile, timeSubjectPaperDTO, password);
		//上传试卷包至华为云OBS
		this.uploadSeaskylightNetsPaper(timeSubjectPaperDTO, originalFilename, zipFile, paper);
		//插入海云天财政部试卷包信息至试卷信息报
		this.insertSeaskylightNetsPaperInfo(paper, timeSubjectPaperDTO);
	}

	/**
	 * 上传海云天财政部试卷包至华为云OBS
	 * @param timeSubjectPaperDTO 试卷vo
	 * @param originalFilename 原始文件名
	 * @param zipFile 试卷包
	 * @param paper 试卷对象
	 * @throws Exception 异常
	 */
	private void uploadSeaskylightNetsPaper(TimeSubjectPaperDTO timeSubjectPaperDTO, String originalFilename, ZipFile zipFile, Paper paper) throws Exception {
		Task task = iTaskService.lambdaQuery()
				.select(Task::getCode)
				.eq(Task::getId, timeSubjectPaperDTO.getTaskId())
				.last("LIMIT 1")
				.one();
		String filePathName = iSysFileService.getFilePathName(task.getCode(), "paper", originalFilename);
		String fileId = iSysFileService
				.uploadHuaweiCloudAndSaveSysFile(originalFilename, filePathName,
						new FileInputStream(zipFile.getFile()), "paper");
		paper.setPaperFileId(fileId);
	}

	/**
	 * 插入海云天财政部试卷包信息至试卷信息报
	 * @param paper 试卷信息对象
	 * @param timeSubjectPaperDTO 试卷vo
	 */
	private void insertSeaskylightNetsPaperInfo(Paper paper, TimeSubjectPaperDTO timeSubjectPaperDTO) {
		//插入试卷表
		if (!save(paper)) {
			log.error("插入试卷表失败");
			throw new KqException(ResponseCode.ERROR_PAPER_SAVE_FAIL.getCode(),
					ResponseCode.ERROR_PAPER_SAVE_FAIL.getMsg());
		}
		//插入批次科目试卷表
		TimeSubjectPaper timeSubjectPaper = new TimeSubjectPaper();
		BeanUtils.copyProperties(timeSubjectPaperDTO, timeSubjectPaper);
		timeSubjectPaper.setPaperId(paper.getId());
		if (!iTimeSubjectPaperService.save(timeSubjectPaper)) {
			log.error("插入批次科目试卷表失败");
			throw new KqException(ResponseCode.ERROR_PAPER_SAVE_FAIL.getCode(),
					ResponseCode.ERROR_PAPER_SAVE_FAIL.getMsg());
		}
	}

	/**
	 * 更新海云天财政部试卷批次编号
	 * @param zipFile 试卷包
	 * @param timeSubjectPaperDTO 试卷vo
	 * @throws IOException 异常
	 */
	private Paper updateSeaskylightNetsPaperTimeCode(ZipFile zipFile, TimeSubjectPaperDTO timeSubjectPaperDTO, StringBuffer password) throws KqException, IOException {
		File tempPaperInfo = null;
		File tempInsideZipFile = null;
		File tempTestPaper = null;
		Paper paper = new Paper();
		ZipFile insideZipFile = null;
		try {
			//更新paperinfo.xml批次编号
			FileHeader paperInfoFileHeader = zipFile.getFileHeader("paperinfo.xml");
			ZipInputStream paperInfoInputStream = zipFile.getInputStream(paperInfoFileHeader);
			String paperInfoContent = FileUtil.getFileContent(paperInfoInputStream);
			//校验试卷
			paperInfoContent  = paperInfoContent.replaceAll("\uFEFF","");
			this.verifySeaskylightNetsPaper(paperInfoContent, timeSubjectPaperDTO);
			paper.setCode(XmlUtil.readXmlOneNode(paperInfoContent,"paperInfo/paperList/row/paperid"));
			paperInfoContent = paperInfoContent.replaceAll("<test_pc>.*</test_pc>",
					"<test_pc>" +
							(org.apache.commons.lang3.StringUtils.isEmpty(timeSubjectPaperDTO.getTimeCode()) ? "0" : timeSubjectPaperDTO.getTimeCode())
							+ "</test_pc>");
			//创建新的paperinfo.xml临时文件
			String newPaperInfoName = "paper_info_" + UUIDUtils.newSortUUID();
			tempPaperInfo = File.createTempFile(newPaperInfoName, ".xml");
			log.info("paperinfo.xml临时文件所在路径: " + tempPaperInfo.getAbsolutePath());
			//写入新的paperinfo.xml临时文件中
			if (!XmlUtil.writeFile(paperInfoContent, tempPaperInfo)) {
				throw new KqException(ResponseCode.ERROR_PAPER_PAPERINFO_IMPORT.getCode(),
						ResponseCode.ERROR_PAPER_PAPERINFO_IMPORT.getMsg());
			}
			//获取内层zip包文件
			FileHeader insideFileHeader = zipFile.getFileHeaders().stream()
					.filter(fileHeader -> "zip".equals(fileHeader.getFileName().substring(fileHeader.getFileName().lastIndexOf(".") + 1)))
					.findFirst()
					.orElseThrow(() -> new KqException(ResponseCode.ERROR_GET_PAPER_ZIP.getCode(),
							ResponseCode.ERROR_GET_PAPER_ZIP.getMsg()));
			String insideZipFileName = insideFileHeader.getFileName();
			ZipInputStream insideZipFileInputStream = zipFile.getInputStream(insideFileHeader);
			//创建内层zip包临时文件
			String newInsideZipFileName = "insideZipFile_" + UUIDUtils.newSortUUID();
			tempInsideZipFile = File.createTempFile(newInsideZipFileName, ".zip");
			FileUtil.writeFile(insideZipFileInputStream, tempInsideZipFile);
			log.info("内层zip包临时文件所在路径: " + tempInsideZipFile.getAbsolutePath());
			//更新内层zip包中test_paper.xml的批次编号
			insideZipFile = new ZipFile(tempInsideZipFile, password.toString().toCharArray());
			FileHeader testPaperFileHeader = insideZipFile.getFileHeader("test_paper.xml");
			ZipInputStream testPaperZipInputStream = insideZipFile.getInputStream(testPaperFileHeader);
			String testPaperContent = FileUtil.getFileContent(testPaperZipInputStream);
			paper.setLanguage(new Integer(Objects.requireNonNull(XmlUtil.readXmlOneNode(testPaperContent, "test_paper/row/paper_lang"))))
					.setName(XmlUtil.readXmlOneNode(testPaperContent, "test_paper/row/test_name"));
			testPaperContent = testPaperContent.replaceAll("<test_pc>.*</test_pc>",
					"<test_pc>" +
							(org.apache.commons.lang3.StringUtils.isEmpty(timeSubjectPaperDTO.getTimeCode()) ? "0" : timeSubjectPaperDTO.getTimeCode())
							+ "</test_pc>");
			//创建新的test_paper.xml临时文件
			String newTestPaperName = "test_paper_" + UUIDUtils.newSortUUID();
			tempTestPaper = File.createTempFile(newTestPaperName, ".xml");
			log.info("test_paper.xml临时文件所在路径: " + tempTestPaper.getAbsolutePath());
			//写入新的test_paper.xml临时文件中
			if (!XmlUtil.writeFile(testPaperContent, tempTestPaper)) {
				throw new KqException(ResponseCode.ERROR_PAPER_TEST_PAPER.getCode(),
						ResponseCode.ERROR_PAPER_TEST_PAPER.getMsg());
			}
			//删除旧的test_paper.xml
			insideZipFile.removeFile(testPaperFileHeader);
			//放入新的test_paper.xml
			insideZipFile.addFile(tempTestPaper, FileUtil.getZipParameters());
			//重命名新的test_paper.xml
			insideZipFile.renameFile(tempTestPaper.getName(), "test_paper.xml");
			//删除旧的内层zip包
			zipFile.removeFile(insideFileHeader);
			//放入新的内层zip包
			zipFile.addFile(tempInsideZipFile, FileUtil.getZipParameters());
			//重命名新的内层zip包
			zipFile.renameFile(tempInsideZipFile.getName(), insideZipFileName);
			//删除旧的paperinfo.xml
			zipFile.removeFile(paperInfoFileHeader);
			//放入新的paperinfo.xml
			zipFile.addFile(tempPaperInfo, FileUtil.getZipParameters());
			//重命名新的paperinfo.xml
			zipFile.renameFile(tempPaperInfo.getName(), "paperinfo.xml");
		} finally {
			//删除临时文件
			if (tempPaperInfo != null) {
				if (!tempPaperInfo.delete()) {
					log.error("删除paperinfo.xml临时文件失败，临时文件目录: "
							+ tempPaperInfo.getAbsolutePath());
				}
				if (tempTestPaper != null) {
					if (!tempTestPaper.delete()) {
						log.error("删除test_paper.xml临时文件失败，临时文件目录: "
								+ tempTestPaper.getAbsolutePath());
					}
				}
			}
			if (tempInsideZipFile != null) {
				if (!tempInsideZipFile.delete()) {
					log.error("删除内层zip包临时文件失败，临时文件目录: "
							+ tempInsideZipFile.getAbsolutePath());
				}
			}
			if (insideZipFile != null) {
				insideZipFile.close();
			}
		}
		paper.setTaskId(timeSubjectPaperDTO.getTaskId())
				.setVersion(1)
				.setType(CodeEnum.FORMAL_PAPER.getCode())
				.setSubjectId(timeSubjectPaperDTO.getSubjectId())
				.setSubjectCode(timeSubjectPaperDTO.getSubjectCode())
				.setStatus(false);
		return paper;
	}

	/**
	 * 校验海云天财政部试卷
	 * @param paperInfo paperinfo.xml文本内容
	 * @param timeSubjectPaperDTO 试卷vo
	 * @throws KqException 异常
	 */
	private void verifySeaskylightNetsPaper(String paperInfo, TimeSubjectPaperDTO timeSubjectPaperDTO) throws KqException {
		//去除非法字符集\uFEFF"
//		String paperInfoXml = paperInfo.replaceAll("\uFEFF","");
		//获取相关节点数据
		String subjectCode = XmlUtil.readXmlOneNode(paperInfo,"paperInfo/paperList/row/subject");
		//校验节点数据
		if (!timeSubjectPaperDTO.getSubjectCode().equals(subjectCode)) {
			throw new KqException(ResponseCode.ERROR_PAPER_SUBJECT_MISMATCH.getCode(),
					ResponseCode.ERROR_PAPER_SUBJECT_MISMATCH.getMsg());
		}
	}

	/**
	 * 保存山大欧玛试卷
	 * @param zipFile 试卷包
	 * @param timeSubjectPaperDTO 试卷管理分页VO类
	 * @param password 密码
	 * @param file 试卷文件
	 * @throws Exception 异常
	 */
	private void saveOumaPaper(ZipFile zipFile, TimeSubjectPaperDTO timeSubjectPaperDTO,
							   StringBuffer password, MultipartFile file) throws Exception {
		//山大欧玛试卷包，只需要添加批次编号即可。
		String testPaperContent = updateOumaPaperTimeCode(zipFile, timeSubjectPaperDTO.getTimeCode());
		zipFile.setPassword(password.toString().toCharArray());
		Task task = iTaskService.lambdaQuery()
				.eq(Task::getId, timeSubjectPaperDTO.getTaskId())
				.last("LIMIT 1")
				.one();
		//4.读取获取zip流进行上传
		InputStream inputStream = new FileInputStream(zipFile.getFile());
		String fileNameOriginal = file.getOriginalFilename();
		String filePathName = iSysFileService.getFilePathName(task.getCode(), "paper", fileNameOriginal);
		String fileId = iSysFileService.uploadHuaweiCloudAndSaveSysFile(fileNameOriginal, filePathName, inputStream, "paper");
		zipFile.close();
		//7.解析试卷并入库
		parsePaperContentInsert(testPaperContent, timeSubjectPaperDTO, fileId);
	}

	/**
	 * 山大欧玛试卷添加批次信息
	 * @param zipFile 试卷包
	 * @param timeCode 批次编号
	 * @return 0：修改成功，其他失败
	 */
	private String updateOumaPaperTimeCode(ZipFile zipFile, String timeCode) throws Exception {
		File newPaper = null;
		String content;
		BufferedWriter out = null;
		try {
			//创建新test_paper.xml
			String newPaperName = "test_paper_" + UUIDUtils.newSortUUID();
			newPaper = File.createTempFile(newPaperName, ".xml");
			//获取源文件内容
			FileHeader fileHeader = zipFile.getFileHeader("test_paper.xml");
			ZipInputStream inputStream = zipFile.getInputStream(fileHeader);
			content = FileUtil.getFileContent(inputStream);
			//修改文件内容
			content = content.replaceAll("<test_pc>.*</test_pc>",
					"<test_pc>" + (StringUtils.isBlank(timeCode) ? 0 : timeCode) + "</test_pc>");
			//写入新文件
			out = new BufferedWriter(new FileWriter(newPaper));
			out.write(content);
			//删除原test_paper.xml
			zipFile.removeFile(fileHeader);
			//加入新test_paper.xml
			zipFile.addFile(newPaper,FileUtil.getZipParameters());
			//重命名
			zipFile.renameFile(newPaper.getName(), "test_paper.xml");
		} finally {
			if (newPaper != null) {
				if (!newPaper.delete()) {
					log.error("删除临时文件失败");
				}
			}
			if (out != null) {
				out.close();
			}
		}
		return content;
	}

	private void parsePaperContentInsert(String testPaperContent, TimeSubjectPaperDTO timeSubjectPaperDTO, String fileId) {
		if (StringUtils.isBlank(testPaperContent)) {
			throw new KqException(ResponseCode.ERROR_PAPER_CONTENT_INVALID.getCode(),
					ResponseCode.ERROR_PAPER_CONTENT_INVALID.getMsg());
		}
		//1.去除非法字符集\uFEFF"
		String xml = testPaperContent.replaceAll("\uFEFF","");
		//2.获取相关节点数据
		String paperCode = XmlUtil.readXmlOneNode(xml,"test_paper/row/test_code");
		String paperName = XmlUtil.readXmlOneNode(xml,"test_paper/row/test_name");
		String subjectCode = XmlUtil.readXmlOneNode(xml,"test_paper/row/subject");
		//3.考卷编码&考卷名称&科目都不能为空
		if (StrUtil.hasBlank(paperCode, paperName, subjectCode)) {
			throw new KqException(ResponseCode.ERROR_PAPER_CONTENT_INVALID.getCode(),
					ResponseCode.ERROR_PAPER_CONTENT_INVALID.getMsg());
		}
		//4.获取试卷内的科目编码，和PaperVO中的科目编码相同才允许导入。
		if (!timeSubjectPaperDTO.getSubjectCode().equals(subjectCode)) {
			throw new KqException(ResponseCode.ERROR_PAPER_SUBJECT_MISMATCH.getCode(),
					ResponseCode.ERROR_PAPER_SUBJECT_MISMATCH.getMsg());
		}
		Paper paper = new Paper();
		paper.setTaskId(timeSubjectPaperDTO.getTaskId())
				.setCode(paperCode)
				.setVersion(1)
				.setType(CodeEnum.FORMAL_PAPER.getCode())
				.setLanguage(1)
				.setName(paperName)
				.setSubjectId(timeSubjectPaperDTO.getSubjectId())
				.setSubjectCode(timeSubjectPaperDTO.getSubjectCode())
				.setPaperFileId(fileId)
				.setStatus(false);
		//插入试卷表
		if (!save(paper)) {
			log.error("插入试卷表失败");
			throw new KqException(ResponseCode.ERROR_PAPER_SAVE_FAIL.getCode(),
					ResponseCode.ERROR_PAPER_SAVE_FAIL.getMsg());
		}
		//插入批次科目试卷表
		TimeSubjectPaper timeSubjectPaper = new TimeSubjectPaper();
		BeanUtils.copyProperties(timeSubjectPaperDTO, timeSubjectPaper);
		timeSubjectPaper.setPaperId(paper.getId());
		if (!iTimeSubjectPaperService.save(timeSubjectPaper)) {
			log.error("插入批次科目试卷表失败");
			throw new KqException(ResponseCode.ERROR_PAPER_SAVE_FAIL.getCode(),
					ResponseCode.ERROR_PAPER_SAVE_FAIL.getMsg());
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void importAuthCode(MultipartFile file, String paperId) throws Exception {
		//获取试卷数据
		Paper paper = this.lambdaQuery()
				.eq(Paper::getId, paperId)
				.eq(Paper::getDelFlag, "0")
				.last("LIMIT 1")
				.one();
		//校验试卷授权码是否已导入
		boolean hasAuthCode = Optional.ofNullable(paper)
				.map(p -> StringUtils.isNotBlank(p.getAuthCodeFileId()))
				//试卷不存在
				.orElseThrow(() -> new KqException(ResponseCode.ERROR_PAPER_NON_EXISTENT_OR_PAPER_NOT_IMPORT.getCode(),
						ResponseCode.ERROR_PAPER_NON_EXISTENT_OR_PAPER_NOT_IMPORT.getMsg()));
		if (hasAuthCode) {
			//已导入过授权码的试卷不能重复导入
			throw new KqException(ResponseCode.ERROR_PAPER_AUTH_PACKAGE_EXISTENT.getCode(),
					ResponseCode.ERROR_PAPER_AUTH_PACKAGE_EXISTENT.getMsg());
		}
		//导入不同类型的授权码
		File tempFile = null;
		try {
			//创建临时文件
	        tempFile = File.createTempFile("sj_file_" + UUIDUtils.newSortUUID(), ".zip");
	        log.debug("#授权码临时文件所在本地路径: {}", tempFile.getAbsolutePath());
	        file.transferTo(tempFile);
			ZipFile zipFile = getZipFile(tempFile, null);
			//分析试卷授权码类型
			switch (this.analysisPaperAuthCodeType(zipFile)) {
				//保存海云天财政部试卷授权码
				case SEASKYLAND_NETS:
					this.saveSeaskylightNetsAuthCode(zipFile, paper, file.getOriginalFilename());
					break;
				//保存山大欧玛试卷授权码
				case OUMA_SOFT:
					this.saveOumaAuthCode(zipFile, paper, file, paperId);
					break;
				//导入的授权码为暂不支持的类型
				default:
					throw new KqException(ResponseCode.PAPER_AUTH_CODE_TYPE_DOES_NOT_SUPPORT.getCode(),
							ResponseCode.PAPER_AUTH_CODE_TYPE_DOES_NOT_SUPPORT.getMsg());
			}
		} finally {
            //删除临时文件
			if (tempFile != null) {
				if (!tempFile.delete()) {
					log.error("删除临时文件失败，所在路径: "
							+ tempFile.getAbsolutePath());
				}
			}
        }
	}

	/**
	 * 保存海云天财政部试卷授权码
	 * @param zipFile 授权码包
	 * @param paper 试卷对象
	 * @param OriginalFilename 原始文件名
	 * @throws Exception 异常
	 */
	private void saveSeaskylightNetsAuthCode(ZipFile zipFile, Paper paper,
											 String OriginalFilename) throws Exception {
		FileHeader fileHeader = zipFile.getFileHeader("codeinfo.xml");
		String content = Optional.ofNullable(fileHeader)
				.map(header -> {
					try {
						ZipInputStream inputStream = zipFile.getInputStream(header);
						return FileUtil.getFileContent(inputStream);
					} catch (Exception e) {
						return null;
					}
				}).orElseThrow(() -> new KqException(ResponseCode.ERROR_PAPER_AUTH_IMPORT.getCode(),
						ResponseCode.ERROR_PAPER_AUTH_IMPORT.getMsg()));
		if (StringUtils.isBlank(content)) {
			//授权码包内容异常
			throw new KqException(ResponseCode.ERROR_PAPER_AUTH_PACKAGE_CONTENT_INVALID.getCode(),
					ResponseCode.ERROR_PAPER_AUTH_PACKAGE_CONTENT_INVALID.getMsg());
		}
		//获取试卷编号
		String paperCode = XmlUtil.readXmlOneNode(content, "codeInfo/paperKeyList/row/paperid");
		//校验试卷编号
		if (!paper.getCode().equals(paperCode)) {
			throw new KqException(ResponseCode.ERROR_AUTH_CODE_NOT_MATCH_PAPER_EXISTENT.getCode(),
					ResponseCode.ERROR_AUTH_CODE_NOT_MATCH_PAPER_EXISTENT.getMsg());
		}
		//获取授权码
		String authCode = XmlUtil.readXmlOneNode(content, "codeInfo/paperKeyList/row/sjmm");
		if (StringUtils.isBlank(authCode)) {
			//导入的授权码包中没有授权码
			throw new KqException(ResponseCode.PAPER_AUTH_CODE_NOT_FOUND.getCode(),
					ResponseCode.PAPER_AUTH_CODE_NOT_FOUND.getMsg());
		}
		//根据任务id获取任务
		Task task = iTaskService.lambdaQuery()
				.select(Task::getCode)
				.eq(Task::getId, paper.getTaskId())
				.last("LIMIT 1")
				.one();
		//上传授权码文件至华为云OBS
		InputStream zipFileInputStream = new FileInputStream(zipFile.getFile());
		String filePathName = iSysFileService.getFilePathName(task.getCode(),
				"paperAuth", OriginalFilename);
		String fileId = iSysFileService.uploadHuaweiCloudAndSaveSysFile(OriginalFilename,
				filePathName, zipFileInputStream, "paper");
		zipFile.close();
		//更新试卷中的授权码及授权码文件id
		boolean success = this.lambdaUpdate()
				.eq(Paper::getId, paper.getId())
				.set(Paper::getAuthCode, authCode)
				.set(Paper::getAuthCodeFileId, fileId)
				.update();
		if (!success) {
			log.error("更新试卷表失败");
			throw new KqException(ResponseCode.ERROR_PAPER_AUTH_SAVE_FAIL.getCode(),
					ResponseCode.ERROR_PAPER_AUTH_SAVE_FAIL.getMsg());
		}
	}

	/**
	 * 保存山大欧玛试卷授权码
	 * @param zipFile 授权码包
	 * @param paper 试卷对象
	 * @param file 授权码源文件
	 * @param paperId 试卷id
	 * @throws Exception 异常
	 */
	private void saveOumaAuthCode(ZipFile zipFile, Paper paper,
								  MultipartFile file, String paperId) throws Exception {
		//获取源文件内容
		List<FileHeader> fileHeaders = zipFile.getFileHeaders();
		ZipInputStream inputStream = zipFile.getInputStream(fileHeaders.get(0));
		String content = FileUtil.getFileContent(inputStream);
		if (StringUtils.isBlank(content)) {
			throw new KqException(ResponseCode.ERROR_PAPER_AUTH_PACKAGE_CONTENT_INVALID.getCode(),
					ResponseCode.ERROR_PAPER_AUTH_PACKAGE_CONTENT_INVALID.getMsg());
		}
		content = TextUtil.stringTrm(content);
		String[] authArr = content.split("#");
		//如果授权码中的试卷编号与试卷编号不匹配抛出异常提示
		if (!authArr[0].equals(paper.getCode())) {
			throw new KqException(ResponseCode.ERROR_AUTH_CODE_NOT_MATCH_PAPER_EXISTENT.getCode(),
					ResponseCode.ERROR_AUTH_CODE_NOT_MATCH_PAPER_EXISTENT.getMsg());
		}
		//获取源文件名
		String fileNameOriginal = file.getOriginalFilename();
		//3.根据任务id获取任务
		Task task = iTaskService.lambdaQuery()
				.eq(Task::getId, paper.getTaskId())
				.last("LIMIT 1")
				.one();
		InputStream zipFileInputStream = new FileInputStream(zipFile.getFile());
		String filePathName = iSysFileService.getFilePathName(task.getCode(),
				"paperAuth", fileNameOriginal);
		String fileId = iSysFileService.uploadHuaweiCloudAndSaveSysFile(fileNameOriginal,
				filePathName, zipFileInputStream, "paper");
		zipFile.close();
		//更新试卷中的授权码及授权码文件id
		boolean update = this.lambdaUpdate()
				.eq(Paper::getId, paperId)
				.set(Paper::getAuthCode, authArr[1])
				.set(Paper::getAuthCodeFileId, fileId)
				.update();
		//若更新失败
		if (!update) {
			throw new KqException(ResponseCode.ERROR_PAPER_AUTH_SAVE_FAIL.getCode(),
					ResponseCode.ERROR_PAPER_AUTH_SAVE_FAIL.getMsg());
		}
	}


	/**
	 * 分析试卷授权码类型
	 * @param zipFile 试卷授权码包
	 * @return 试卷授权码类型
	 * @throws ZipException 异常
	 */
	private PaperTypeEnum analysisPaperAuthCodeType(ZipFile zipFile) throws Exception {
		List<FileHeader> fileHeaders = zipFile.getFileHeaders();
		boolean hasCodeInfo = fileHeaders.stream()
				.anyMatch(fileHeader -> "codeinfo.xml".equals(fileHeader.getFileName()));
		return hasCodeInfo ? PaperTypeEnum.SEASKYLAND_NETS : PaperTypeEnum.OUMA_SOFT;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteByTimeSubject(PaperDeleteParam paperDeleteParam) {
		// 目前只支持一个批次科目下绑定一张试卷
		this.deleteByTimeSubject(paperDeleteParam.getTimeSubjectId(), paperDeleteParam.getPaperId());
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteMockPaperByTimeSubject(PaperDeleteParam paperDeleteParam) {
		this.deleteByTimeSubject(paperDeleteParam.getTimeSubjectId(), paperDeleteParam.getPaperId());
	}

	private void deleteByTimeSubject(String timeSubjectId, String paperId) {
		QueryWrapper<Paper> paperQueryWrapper = new QueryWrapper<>();
		paperQueryWrapper.lambda()
				.eq(Paper::getId, paperId)
				.eq(Paper::getDelFlag, false);
		QueryWrapper<TimeSubjectPaper> timeSubjectPaperQueryWrapper = new QueryWrapper<>();
		timeSubjectPaperQueryWrapper.lambda()
				.eq(TimeSubjectPaper::getTimeSubjectId, timeSubjectId)
				.eq(TimeSubjectPaper::getPaperId, paperId)
				.eq(TimeSubjectPaper::getDelFlag, false);
		//删除试卷表
		if (!remove(paperQueryWrapper)) {
			throw new KqException(ResponseCode.ERROR_TASK_DELETE_PAPER.getCode(),
					"删除试卷表失败");
		}
		//删除批次科目试卷表
		if (!iTimeSubjectPaperService.remove(timeSubjectPaperQueryWrapper)) {
			throw new KqException(ResponseCode.ERROR_TASK_DELETE_PAPER.getCode(),
					"删除批次科目试卷表失败");
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void updatePaperStatus(PaperParam paperParam) {
		if (StringUtils.isBlank(paperParam.getPaperId())) {
			throw new KqException(ResponseCode.ERROR_PAPER_UPDATE_STATUS.getCode(),
					ResponseCode.ERROR_PAPER_UPDATE_STATUS.getMsg());
		}
		boolean update = this.lambdaUpdate()
				.eq(Paper::getId, paperParam.getPaperId())
				.eq(Paper::getTaskId, paperParam.getTaskId())
				.eq(Paper::getDelFlag, false)
				.isNotNull(Paper::getPaperFileId)
				.set(Paper::getStatus, paperParam.isStatus())
				.update();
		//如果更新失败 则抛出异常
		if (!update) {
			throw new KqException(ResponseCode.PAPER_EXISTENT_CANNOT_DISABLED.getCode(),
					ResponseCode.PAPER_EXISTENT_CANNOT_DISABLED.getMsg());
		}
	}

	@Override
	public List<PaperFileDTO> getForExamSystem(String taskCode, String roomCode, String timeCode, Integer paperType) {
		return baseMapper.getForExamSystem(taskCode, roomCode, timeCode, paperType);
	}

	@Override
	public List<PaperFileDTO> getPaperAuthorizationCode(String taskCode, String roomCode, String timeCode, Integer paperType, String paperCodes) {
		return baseMapper.getPaperAuthorizationCode(taskCode, roomCode, timeCode, paperType, paperCodes);
	}

	@Override
	public PaperFileDTO getObtMockPaper(String taskCode, Integer paperType) {
		return baseMapper.getObtMockPaper(taskCode, paperType);
	}

	@Override
	public PaperFileDTO getObtMockPaperAuthorizationCode(String taskCode, String paperCode, Integer paperType) {
		return baseMapper.getObtMockPaperAuthorizationCode(taskCode, paperCode, paperType);
	}

	@Override
	public List<Paper> listByTaskId(String taskId) {
		QueryWrapper<Paper> queryWrapper = new QueryWrapper<>();
		queryWrapper.lambda()
				.eq(Paper::getTaskId, taskId)
				.eq(Paper::getDelFlag, false);
		return list(queryWrapper);
	}

	@Override
	public Page<PaperVO> mockPaperPage(PageParam<PaperVO> pageParam, String taskId) {
		return baseMapper.page(pageParam, taskId, CodeEnum.MOCK_PAPER.getCode());
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void importMockPaper(MultipartFile file, String taskId, String timeSubjectId) throws Exception {
		Task task = iTaskService.lambdaQuery()
				.select(Task::getCode)
				.eq(Task::getId, taskId)
				.eq(Task::getDelFlag, false)
				.last("LIMIT 1")
				.one();
		//2.任务必须存在
		if (ObjectUtils.isEmpty(task)) {
			throw new KqException(ResponseCode.ERROR_MOCK_PAPER_TASK_ID_NON_EXISTENT.getCode(),
					ResponseCode.ERROR_MOCK_PAPER_TASK_ID_NON_EXISTENT.getMsg());
		}
		//校验当前批次科目下是否存在模拟试卷
		Boolean hasMockPaper = baseMapper.hasPaper(taskId, timeSubjectId, CodeEnum.MOCK_PAPER.getCode());
		Optional.ofNullable(hasMockPaper).map(has -> {
			if (has) {
				//当前批次科目下已存在模拟试卷
				throw new KqException(ResponseCode.ERROR_MOCK_PAPER_IN_TASK_EXISTENT.getCode(),
						ResponseCode.ERROR_MOCK_PAPER_IN_TASK_EXISTENT.getMsg());
			}
			return false;
		}).orElseThrow(() -> new KqException(ResponseCode.ERROR_PAPER_IMPORT.getCode(), "校验当前批次科目下是否存在模拟试卷失败")
		        );
		//区分不同类型模拟试卷导入
		File tempFile = null;
		try {
			//1.创建临时文件
			tempFile = File.createTempFile("sj_file_" + UUIDUtils.newSortUUID(), ".zip");
			log.info("#试卷临时文件所在本地路径: {}", tempFile.getAbsolutePath());
			file.transferTo(tempFile);
			//2.解密试卷，修改文件，加密试卷
			StringBuffer password = new StringBuffer();
			ZipFile zipFile = getZipFile(tempFile, password);
			//解析试卷类型(海云天试卷、山大欧玛试卷...)
			switch (this.analysisPaperType(zipFile)) {
				//保存海云天财政部模拟试卷
				case SEASKYLAND_NETS:
					this.saveSeaskylandNestMockPaper(zipFile, password, file.getOriginalFilename(),
							task, taskId, timeSubjectId);
					break;
				//保存山大欧玛模拟试卷
				case OUMA_SOFT:
					this.saveOumaMockPaper(zipFile, password, file, task, taskId, timeSubjectId);
					break;
				default:
					throw new KqException(ResponseCode.PAPER_TYPE_DOES_NOT_SUPPORT.getCode(),
							ResponseCode.PAPER_TYPE_DOES_NOT_SUPPORT.getMsg());
			}
		} finally {
			//删除临时文件
			if (null != tempFile) {
				if (!FileUtil.deleteFileOrDir(tempFile)) {
					log.error("删除模拟试卷临时文件失败，临时文件目录: "
							+ tempFile.getAbsolutePath());
				}
			}
		}
	}

	/**
	 * 保存海云天财政部模拟试卷
	 * @param zipFile 试卷包文件
	 * @param password 密码
	 * @param originalFilename 试卷包原文件名
	 * @param task 考试任务
	 * @param taskId 考试任务id
	 * @throws Exception 异常
	 */
	private void saveSeaskylandNestMockPaper(ZipFile zipFile, StringBuffer password, String originalFilename,
											 Task task, String taskId, String timeSubjectId) throws Exception {
		File tempInsideZipFile = null;
		File tempPaperInfo = null;
		File tempTestPaper = null;
		ZipFile insideZipFile = null;
		//上传至华为云OBS
		try (InputStream inputStream = new FileInputStream(zipFile.getFile());){
			//获取内层zip包文件
			FileHeader insideFileHeader = zipFile.getFileHeaders().stream()
					.filter(fileHeader -> "zip".equals(fileHeader.getFileName().substring(fileHeader.getFileName().lastIndexOf(".") + 1)))
					.findFirst()
					.orElseThrow(() -> new KqException(ResponseCode.ERROR_GET_PAPER_ZIP.getCode(),
							ResponseCode.ERROR_GET_PAPER_ZIP.getMsg()));
			String insideZipFileName = insideFileHeader.getFileName();
			ZipInputStream insideZipFileInputStream = zipFile.getInputStream(insideFileHeader);
			//创建内层zip包临时文件
			String newInsideZipFileName = "insideZipFile_" + UUIDUtils.newSortUUID();
			tempInsideZipFile = File.createTempFile(newInsideZipFileName, ".zip");
			FileUtil.writeFile(insideZipFileInputStream, tempInsideZipFile);
			log.info("内层zip包临时文件所在路径: " + tempInsideZipFile.getAbsolutePath());
			//获取test_paper.xml
			insideZipFile = new ZipFile(tempInsideZipFile, password.toString().toCharArray());
			FileHeader testPaperFileHeader = insideZipFile.getFileHeader("test_paper.xml");
			ZipInputStream testPaperZipInputStream = insideZipFile.getInputStream(testPaperFileHeader);
			String testPaperContent = FileUtil.getFileContent(testPaperZipInputStream);
			//去除非法字符集\uFEFF
			testPaperContent = testPaperContent.replaceAll("\uFEFF","");
			//校验科目是否匹配
			Subject subject = iSubjectService.getByTimeSubjectId(timeSubjectId);
			String code = Optional.ofNullable(subject)
					.map(Subject::getCode)
					.orElseThrow(() -> new KqException(ResponseCode.ERROR_MOCK_PAPER_SUBJECT_CODE_NON_EXISTENT.getCode(),
							ResponseCode.ERROR_MOCK_PAPER_SUBJECT_CODE_NON_EXISTENT.getMsg()));
			String subjectCode = XmlUtil.readXmlOneNode(testPaperContent,"test_paper/row/subject");
			if (!code.equals(subjectCode)) {
				throw new KqException(ResponseCode.ERROR_MOCK_PAPER_SUBJECT_CODE_NON_EXISTENT.getCode(),
						"导入试卷的科目与当前批次的科目不一致");
			}
			//获取试卷信息
			String paperCode = XmlUtil.readXmlOneNode(testPaperContent,"test_paper/row/test_code");
			if (StringUtils.isBlank(paperCode)) {
				throw new KqException(ResponseCode.ERROR_MOCK_PAPER_CODE_NONE.getCode(),
						ResponseCode.ERROR_MOCK_PAPER_CODE_NONE.getMsg());
			}
			String paperName = XmlUtil.readXmlOneNode(testPaperContent,"test_paper/row/test_name");
			if (StringUtils.isBlank(paperName)) {
				throw new KqException(ResponseCode.ERROR_MOCK_PAPER_CONTENT_NONE.getCode(),
						ResponseCode.ERROR_MOCK_PAPER_CONTENT_NONE.getMsg());
			}
			//修改试卷包中的批次编号
			//获取批次科目对应的科目编号
			Time time = iTimeService.getByTimeSubjectId(timeSubjectId);
			String timeCode = Optional.ofNullable(time).map(t -> {
				if (t.getCode() != null) {
					return String.valueOf(t.getCode());
				}
				return StringUtils.EMPTY;
			}).orElse(StringUtils.EMPTY);
			//更新paperinfo.xml批次编号
			FileHeader paperInfoFileHeader = zipFile.getFileHeader("paperinfo.xml");
			ZipInputStream paperInfoInputStream = zipFile.getInputStream(paperInfoFileHeader);
			String paperInfoContent = FileUtil.getFileContent(paperInfoInputStream);
			paperInfoContent = paperInfoContent.replaceAll("<test_pc>.*</test_pc>",
					"<test_pc>" +
							(StringUtils.isBlank(timeCode) ? "0" : timeCode)
							+ "</test_pc>");
			//创建新的paperinfo.xml临时文件
			String newPaperInfoName = "paper_info_" + UUIDUtils.newSortUUID();
			tempPaperInfo = File.createTempFile(newPaperInfoName, ".xml");
			log.info("paperinfo.xml临时文件所在路径: " + tempPaperInfo.getAbsolutePath());
			//写入新的paperinfo.xml临时文件中
			if (!XmlUtil.writeFile(paperInfoContent, tempPaperInfo)) {
				throw new KqException(ResponseCode.ERROR_PAPER_PAPERINFO_IMPORT.getCode(),
						ResponseCode.ERROR_PAPER_PAPERINFO_IMPORT.getMsg());
			}
			//更新内层zip包中test_paper.xml的批次编号
			testPaperContent = testPaperContent.replaceAll("<test_pc>.*</test_pc>",
					"<test_pc>" +
							(StringUtils.isBlank(timeCode) ? "0" : timeCode)
							+ "</test_pc>");
			//创建新的test_paper.xml临时文件
			String newTestPaperName = "test_paper_" + UUIDUtils.newSortUUID();
			tempTestPaper = File.createTempFile(newTestPaperName, ".xml");
			log.info("test_paper.xml临时文件所在路径: " + tempTestPaper.getAbsolutePath());
			//写入新的test_paper.xml临时文件中
			if (!XmlUtil.writeFile(testPaperContent, tempTestPaper)) {
				throw new KqException(ResponseCode.ERROR_PAPER_TEST_PAPER.getCode(),
						ResponseCode.ERROR_PAPER_TEST_PAPER.getMsg());
			}
			//删除旧的test_paper.xml
			insideZipFile.removeFile(testPaperFileHeader);
			//放入新的test_paper.xml
			insideZipFile.addFile(tempTestPaper, FileUtil.getZipParameters());
			//重命名新的test_paper.xml
			insideZipFile.renameFile(tempTestPaper.getName(), "test_paper.xml");
			//删除旧的内层zip包
			zipFile.removeFile(insideFileHeader);
			//放入新的内层zip包
			zipFile.addFile(tempInsideZipFile, FileUtil.getZipParameters());
			//重命名新的内层zip包
			zipFile.renameFile(tempInsideZipFile.getName(), insideZipFileName);
			//删除旧的paperinfo.xml
			zipFile.removeFile(paperInfoFileHeader);
			//放入新的paperinfo.xml
			zipFile.addFile(tempPaperInfo, FileUtil.getZipParameters());
			//重命名新的paperinfo.xml
			zipFile.renameFile(tempPaperInfo.getName(), "paperinfo.xml");
			
			String filePathName = iSysFileService.getFilePathName(task.getCode(), "paper", originalFilename);
			String fileId = iSysFileService.uploadHuaweiCloudAndSaveSysFile(originalFilename, filePathName, inputStream, "paper");
			//设置试卷信息
			Paper paper = new Paper();
			paper.setTaskId(taskId)
					.setCode(paperCode)
					.setVersion(1)
					.setType(CodeEnum.MOCK_PAPER.getCode())
					.setLanguage(new Integer(Objects.requireNonNull(XmlUtil.readXmlOneNode(testPaperContent, "test_paper/row/paper_lang"))))
					.setName(paperName)
					.setSubjectId(subject.getId())
					.setSubjectCode(subject.getCode())
					.setPaperFileId(fileId)
					.setStatus(false);
			//保存试卷信息
			if (!save(paper)) {
				throw new KqException(ResponseCode.ERROR_PAPER_SAVE_FAIL.getCode(),
						ResponseCode.ERROR_PAPER_SAVE_FAIL.getMsg());
			}
			//获取批次科目信息
			TimeSubject timeSubject = iTimeSubjectService.lambdaQuery()
					.eq(TimeSubject::getId, timeSubjectId)
					.eq(TimeSubject::getDelFlag, false)
					.last("LIMIT 1")
					.one();
			//设置批次科目试卷信息
			TimeSubjectPaper timeSubjectPaper = new TimeSubjectPaper();
			timeSubjectPaper.setTaskId(taskId)
					.setTimeId(timeSubject.getTimeId())
					.setSubjectId(timeSubject.getSubjectId())
					.setPaperId(paper.getId())
					.setTimeSubjectId(timeSubjectId);
			//保存批次科目试卷信息
			if (!iTimeSubjectPaperService.save(timeSubjectPaper)) {
				log.error("插入批次科目试卷信息表失败");
				throw new KqException(ResponseCode.ERROR_PAPER_SAVE_FAIL.getCode(),
						ResponseCode.ERROR_PAPER_SAVE_FAIL.getMsg());
			}
		} finally {
			//删除临时文件
			if (tempPaperInfo != null && !tempPaperInfo.delete()) {
				log.error("删除paperinfo.xml临时文件失败，临时文件目录: "
						+ tempPaperInfo.getAbsolutePath());
			}
			if (tempTestPaper != null && !tempTestPaper.delete()) {
				log.error("删除test_paper.xml临时文件失败，临时文件目录: "
						+ tempTestPaper.getAbsolutePath());
			}
			if (tempInsideZipFile != null && !tempInsideZipFile.delete()) {
				log.error("删除zip包临时文件失败，临时文件目录: "
						+ tempInsideZipFile.getAbsolutePath());
			}
			if (insideZipFile != null) {
				insideZipFile.close();
			}
		}
	}

	/**
	 * 保存山大欧玛模拟试卷
	 * @param zipFile 试卷包文件
	 * @param password 密码
	 * @param file 试卷包原始文件
	 * @param task 考试任务
	 * @param taskId 考试任务id
	 * @throws Exception 异常
	 */
	private void saveOumaMockPaper(ZipFile zipFile, StringBuffer password, MultipartFile file,
								   Task task, String taskId, String timeSubjectId) throws Exception {
		String testPaperContent = updateOumaPaperTimeCode(zipFile, null);
		zipFile.setPassword(password.toString().toCharArray());
		//3读取获取zip流进行上传
		InputStream inputStream = new FileInputStream(zipFile.getFile());
		String fileNameOriginal = file.getOriginalFilename();
		String filePathName = iSysFileService.getFilePathName(task.getCode(), "paper", fileNameOriginal);
		String fileId = iSysFileService.uploadHuaweiCloudAndSaveSysFile(fileNameOriginal, filePathName, inputStream, "paper");
		zipFile.close();
		//4.解析试卷并入库
		this.parseMockPaperContentInsert(testPaperContent, taskId, fileId, timeSubjectId);
	}

	/**
	 * 解析并组装模拟试卷
	 * @param testPaperContent
	 * @param taskId
	 * @param fileId
	 */
	private void parseMockPaperContentInsert(String testPaperContent, String taskId, String fileId, String timeSubjectId)  {
		if(TextUtil.isNull(testPaperContent)){
			throw new KqException(ResponseCode.ERROR_PAPER_CONTENT_INVALID.getCode(),
					ResponseCode.ERROR_PAPER_CONTENT_INVALID.getMsg());
		}
		//1.去除非法字符集\uFEFF"
		String xml = testPaperContent.replaceAll("\uFEFF","");
		//2.获取相关节点数据
		String paperCode = XmlUtil.readXmlOneNode(xml,"test_paper/row/test_code");
		String paperName = XmlUtil.readXmlOneNode(xml,"test_paper/row/test_name");
		String subjectCode = XmlUtil.readXmlOneNode(xml,"test_paper/row/subject");
		//3.考卷编码&考卷名称&科目都不能为空
		if (StrUtil.hasBlank(paperCode, paperName, subjectCode)) {
			throw new KqException(ResponseCode.ERROR_PAPER_CONTENT_INVALID.getCode(),
					ResponseCode.ERROR_PAPER_CONTENT_INVALID.getMsg());
		}
		//校验试卷的科目与当前批次的科目是否匹配
		Subject subject = iSubjectService.getByTimeSubjectId(timeSubjectId);
		String code = Optional.ofNullable(subject)
				.map(Subject::getCode)
				.orElseThrow(() -> new KqException(ResponseCode.ERROR_MOCK_PAPER_SUBJECT_CODE_NON_EXISTENT.getCode(),
						ResponseCode.ERROR_MOCK_PAPER_SUBJECT_CODE_NON_EXISTENT.getMsg()));
		if (!code.equals(subjectCode)) {
			throw new KqException(ResponseCode.ERROR_MOCK_PAPER_SUBJECT_CODE_NON_EXISTENT.getCode(),
					"导入试卷的科目与当前批次的科目不一致");
		}
		Paper paper = new Paper();
		paper.setTaskId(taskId)
				.setCode(paperCode)
				.setVersion(1)
				.setType(CodeEnum.MOCK_PAPER.getCode())
				.setLanguage(1)
				.setName(paperName)
				.setSubjectId(subject.getId())
				.setSubjectCode(subject.getCode())
				.setPaperFileId(fileId)
				.setStatus(false);
		//保存试卷表
		if(!save(paper)){
			log.error("保存试卷表失败");
			throw new KqException(ResponseCode.ERROR_PAPER_SAVE_FAIL.getCode(),
					ResponseCode.ERROR_PAPER_SAVE_FAIL.getMsg());
		}
		//获取批次科目信息
		TimeSubject timeSubject = iTimeSubjectService.lambdaQuery()
				.eq(TimeSubject::getId, timeSubjectId)
				.eq(TimeSubject::getDelFlag, false)
				.last("LIMIT 1")
				.one();
		//设置批次科目试卷信息
		TimeSubjectPaper timeSubjectPaper = new TimeSubjectPaper();
		timeSubjectPaper.setTaskId(taskId)
				.setTimeId(timeSubject.getTimeId())
				.setSubjectId(timeSubject.getSubjectId())
				.setPaperId(paper.getId())
				.setTimeSubjectId(timeSubjectId);
		//保存批次科目试卷信息
		if (!iTimeSubjectPaperService.save(timeSubjectPaper)) {
			log.error("插入批次科目试卷信息表失败");
			throw new KqException(ResponseCode.ERROR_PAPER_SAVE_FAIL.getCode(),
					ResponseCode.ERROR_PAPER_SAVE_FAIL.getMsg());
		}
	}

	private ZipFile getZipFile(File file, StringBuffer password) throws IOException {
        ZipFile zipFile = null;
		List<String> paperPasswords = configManager.getPaperPasswords();
		if (paperPasswords == null || paperPasswords.isEmpty()) {
			//没密码
			zipFile = new ZipFile(file);
			if (!checkZipFile(zipFile)) {
				zipFile = null;
			}
		} else {
			//有密码
			for (String pw : paperPasswords) {
				zipFile = new ZipFile(file, pw.toCharArray());
				if (checkZipFile(zipFile)) {
					if (password != null) {
						password.append(pw);
					}
					break;
				} else {
					zipFile = null;
				}
			}
		}
		if (zipFile == null) {
			throw new KqException(ResponseCode.ARRANGE_DECO_FILE_PWD_ERROR.getCode(),ResponseCode.ARRANGE_DECO_FILE_PWD_ERROR.getMsg());
		}
        return zipFile;
	}

	
	private boolean checkZipFile(ZipFile zipFile) throws GovInvalidRequestException, IOException {
		zipFile.setCharset(Charset.forName("GBK"));
		if (!zipFile.isValidZipFile()) {
			return false;
		}
		List<FileHeader> fileHeaders = zipFile.getFileHeaders();
		if (fileHeaders == null || fileHeaders.isEmpty()) {
			return false;
		} else {
			ZipInputStream inputStream = null;
			try {
				FileHeader fileHeader = fileHeaders.get(0);
				inputStream = zipFile.getInputStream(fileHeader);
				inputStream.read();
				FileUtil.getFileContent(inputStream);
				return true;
			} catch (Exception e) {
				return false;
			} finally {
				if (inputStream != null) {
					inputStream.close();
				}
			}
		}
	}
	
}
